package tabletask

import (
	"errors"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"cvevulner/cve-timed-task/db_models"
	"cvevulner/cve-timed-task/util"
	"cvevulner/models"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
)

// DownloadGuessYaml Download the yaml file of openGauss on Gitee
func DownloadGuessYaml(downloadUrl string) (string, error) {
	filePath := beego.AppConfig.String("timedtask::packageDir") + "gauss_yaml/"
	fErr := util.MakeDir(filePath)
	if fErr != nil {
		return "", fErr
	}
	yamlFileName := filePath + "gauss_yaml.yaml"
	//download the yaml file
	resp, gErr := http.Get(downloadUrl)
	defer resp.Body.Close()
	if gErr != nil {
		logs.Error("get", downloadUrl, "error: ", gErr.Error())
		return "", gErr
	}
	if resp.StatusCode != http.StatusOK {
		logs.Error("get", downloadUrl, resp.Status)
		return "", errors.New(resp.Status)
	}
	_ = os.Remove(yamlFileName)
	saveFile, cErr := os.Create(yamlFileName)
	defer saveFile.Close()
	if cErr != nil {
		logs.Error("Create file error ", yamlFileName, cErr.Error())
		return "", cErr
	}
	_, yErr := io.Copy(saveFile, resp.Body)
	if yErr != nil {
		logs.Error("copy resp.Body to file error", yamlFileName, yErr.Error())
		return "", yErr
	}
	return yamlFileName, nil
}

func StoreYamlData(yamlData map[string]map[string]string, ormModel orm.Ormer, warehouse string) {
	if len(yamlData) > 0 {
		for k, v := range yamlData {
			var err error
			if _, ok := v["version"]; !ok || v["version"] == "" {
				v["version"] = ""
			}
			//open transaction
			logs.Info("open transaction")
			tranErr := ormModel.Begin()
			if tranErr != nil {
				logs.Error(" Open transaction error:", tranErr.Error())
				continue
			}
			originData, sErr := db_models.SelectYamlOriginData(k, v["version"], ormModel)
			now := time.Now()
			if sErr != nil {
				if errors.Is(orm.ErrNoRows, sErr) {
					logs.Warn("error is orm.ErrNoRows, Create a record")
					openGuessYaml := &models.OpenGussYaml{
						PackageName: k,
						Version:     v["version"],
						OriginUrl:   v["url"],
						Status:      1,
						CpeName:     v["cpeName"],
						CreateTime:  now.Format("2006-01-02 15:04:05"),
						UpdateTime:  now.Format("2006-01-02 15:04:05"),
						MarkBit:     1,
						Repo:        "security",
						Warehouse:   warehouse,
					}
					sErr = db_models.InsertYamlOriginData(openGuessYaml, ormModel)
					if sErr != nil {
						logs.Error("db_models.InsertYamlOriginData error:", sErr.Error())
						tranErr = ormModel.Rollback()
						if tranErr != nil {
							logs.Error(" Rollback transaction error:", tranErr.Error())
						}
						continue
					}
					logs.Info("insert OpenGussYaml success")
				} else {
					logs.Error("db_models.SelectYamlOriginData error:", err)
					tranErr = ormModel.Rollback()
					if tranErr != nil {
						logs.Error(" Rollback transaction error:", tranErr.Error())
					}
					continue
				}
			} else {
				logs.Info("database records, change the data")
				openGuessYaml := &models.OpenGussYaml{
					Id:         originData.Id,
					OriginUrl:  v["url"],
					Status:     1,
					CpeName:    v["cpeName"],
					UpdateTime: now.Format("2006-01-02 15:04:05"),
					Repo:       "security",
					MarkBit:    1,
					Warehouse:  warehouse,
				}
				_, err = db_models.UpdateYamlOriginData(openGuessYaml, ormModel)
				if err != nil {
					logs.Error("db_models.UpdateYamlOriginData:", err.Error())
					tranErr = ormModel.Rollback()
					if tranErr != nil {
						logs.Error(" Rollback transaction error:", tranErr.Error())
					}
					continue
				}
			}
			packAgesData, rowsAffected, lErr := db_models.SelectYamlData(k, v["version"], ormModel)
			if lErr != nil {
				logs.Error("db_models.SelectYamlData error:", lErr)
				tranErr = ormModel.Rollback()
				if tranErr != nil {
					logs.Error(" Rollback transaction error:", tranErr.Error())
				}
				continue
			}
			if rowsAffected == 0 {
				logs.Warn("No record found, Create a record")
				var packId int64
				gitOpenEuler, yErr := db_models.SelectYamlLastData(ormModel)
				if yErr != nil {
					if errors.Is(orm.ErrNoRows, yErr) {
						packId = 10000000
					} else {
						logs.Error("db_models.SelectYamlLastData error:", yErr)
						tranErr = ormModel.Rollback()
						if tranErr != nil {
							logs.Error(" Rollback transaction error:", tranErr.Error())
						}
						continue
					}
				} else {
					if gitOpenEuler.PackageId >= 10000000 {
						packId = gitOpenEuler.PackageId + 1
					} else {
						packId = 10000000
					}
				}
				goe := &models.GitOpenEuler{
					PackageId:   packId,
					PackageName: k,
					Version:     v["version"],
					OriginUrl:   v["url"],
					CreateTime:  now,
					UpdateTime:  now,
					CpePackName: v["cpeName"],
					Status:      1,
				}
				lastId, iErr := db_models.InsertYamlData(goe, ormModel)
				if iErr != nil {
					logs.Error("db_models.InsertYamlData error:", iErr)
					tranErr = ormModel.Rollback()
					if tranErr != nil {
						logs.Error(" Rollback transaction error:", tranErr.Error())
					}
					continue
				}
				gpi := &models.GitPackageInfo{
					GitId:       lastId,
					PackageName: k,
					Version:     v["version"],
					OriginUrl:   v["url"],
					CreateTime:  now,
					UpdateTime:  now,
					Decription:  "",
					Status:      0,
				}
				err = db_models.InsertYamlDetailData(gpi, ormModel)
				if err != nil {
					logs.Error("db_models.InsertYamlDetailData:", err.Error())
					tranErr = ormModel.Rollback()
					if tranErr != nil {
						logs.Error(" Rollback transaction error:", tranErr.Error())
					}
					continue
				}
			} else {
				if rowsAffected > 1 {
					for _, pv := range packAgesData[1:] {
						err = db_models.DeleteYamlOpenEulerDetailData(pv.GitId, ormModel)
						if err != nil {
							logs.Error("db_models.DeleteYamlOpenEulerDetailData:", err.Error())
							tranErr = ormModel.Rollback()
							if tranErr != nil {
								logs.Error(" Rollback transaction error:", tranErr.Error())
							}
							continue
						}
						err = db_models.DeleteYamlOpenEulerData(pv.GitId, ormModel)
						if err != nil {
							logs.Error("db_models.DeleteYamlOpenEulerData:", err.Error())
							tranErr = ormModel.Rollback()
							if tranErr != nil {
								logs.Error(" Rollback transaction error:", tranErr.Error())
							}
							continue
						}
					}
				}
				goe := &models.GitOpenEuler{
					OriginUrl:   v["url"],
					UpdateTime:  now,
					CpePackName: v["cpeName"],
					Status:      1,
					PackageId:   packAgesData[0].PackageId,
					PackageName: k,
					Version:     v["version"],
				}
				err = db_models.UpdateYamlData(goe, ormModel)
				if err != nil {
					logs.Error("db_models.UpdateYamlData:", err.Error())
					tranErr = ormModel.Rollback()
					if tranErr != nil {
						logs.Error(" Rollback transaction error:", tranErr.Error())
					}
					continue
				}
				gpi := &models.GitPackageInfo{
					PackageName: k,
					Version:     v["version"],
					OriginUrl:   v["url"],
					UpdateTime:  now,
					Status:      0,
					GitId:       packAgesData[0].GitId,
				}
				err = db_models.UpdateYamlDetailData(gpi, ormModel)
				if err != nil {
					logs.Error("db_models.UpdateYamlDetailData", err.Error())
					tranErr = ormModel.Rollback()
					if tranErr != nil {
						logs.Error(" Rollback transaction error:", tranErr.Error())
					}
					continue
				}
			}

			tranErr = ormModel.Commit()
			if tranErr != nil {
				logs.Error(" Commit transaction error:", tranErr.Error())
				continue
			}
			logs.Info("Transaction committed successfully", k)
		}
	} else {
		logs.Warning("yamlData Is empty, nothing can be done")
	}
}

// ProcGaussYaml
//1. Create a folder
//2. Download yaml
//3. Parse yaml
//4. Save in mysql
func ProcGaussYaml() {
	ormModel := orm.NewOrm()
	downloadUrls := []string{
		"https://gitee.com/opengauss/openGauss-third_party/raw/master/Third_Party_Open_Source_Software_List.yaml",
		"https://gitee.com/opengauss/DataStudio/raw/master/Third_Party_Open_Source_Software_List.yaml",
		"https://gitee.com/opengauss/openGauss-connector-jdbc/raw/master/Third_Party_Open_Source_Software_List.yaml",
		"https://gitee.com/opengauss/openGauss-tools-sql-translator/raw/master/Third_Party_Open_Source_Software_List.yaml",
	}
	err := db_models.UpdateYamlOriginMark(ormModel)
	if err != nil {
		logs.Error("db_models.UpdateYamlOriginMark error:", err.Error())
		return
	}
	for _, downloadUrl := range downloadUrls {
		yamlFileName, err := DownloadGuessYaml(downloadUrl)
		if err != nil {
			logs.Error("DownloadGuessYaml error:", err.Error())
			return
		}
		var warehouse string
		if len(strings.Split(downloadUrl, "https://gitee.com/opengauss/")) > 1 {
			warehouse = strings.Split(strings.Split(downloadUrl, "https://gitee.com/opengauss/")[1], "/")[0]
		}
		logs.Info("The download yaml file address is:", yamlFileName)

		yamlData, err := util.ParseYaml(yamlFileName)
		if err != nil {
			logs.Error("util.ParseYaml error:", yamlFileName, err)
			return
		}
		StoreYamlData(yamlData, ormModel, warehouse)
	}
	err = db_models.DeleteYamlOriginMark(ormModel)
	if err != nil {
		logs.Error("db_models.DeleteYamlOriginMark error:", err.Error())
	}
	DeleteYamlData(ormModel)
}
